using System;
using System.IO;
using System.Web;
using System.Xml;
using System.Globalization;
using System.Collections.Generic;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

using RealDolmen.Helpers;
using RealDolmen.SharePoint.Helpers;
using RealDolmen.SharePoint.ListFeatures.Elements;

namespace RealDolmen.SharePoint.ListFeatures
{
    public class ListFeatureDefinition
    {
        #region Variables
        private Guid featureDefinitionId;
        private SPFeatureDefinition featureDefinition;
        internal LFDKeyManager KeyManager;
        #endregion // Variables

        #region Constructors
        protected ListFeatureDefinition(Guid g)
        {
            featureDefinitionId = g;
            KeyManager = new LFDKeyManager();
        }
        protected ListFeatureDefinition(SPFeatureDefinition featureDefinition)
        {
            FeatureDefinition = featureDefinition;
            featureDefinitionId = featureDefinition.Id;
            KeyManager = new LFDKeyManager();
        }
        #endregion // Constructors


        #region ElementFile List
        List<ListFeatureElementsFile> elementFileList;

        public List<ListFeatureElementsFile> ElementFileList
        {
            get
            {
                EnsureElementFileList();

                return elementFileList;
            }
        }
        protected void EnsureElementFileList()
        {
            if (this.elementFileList == null)
            {
                List<ListFeatureElementsFile> list = new List<ListFeatureElementsFile>();
                if (Exists)
                {
                    string WssPrefix = "wss:";

                    //string xpath = "/" + WssPrefix + "Feature/" + WssPrefix + "ElementManifests/" + WssPrefix + "ElementFile";
                    string xpath = WssPrefix + "ElementManifests/" + WssPrefix + "ElementFile";

                    XmlElement xmlElement = (XmlElement)FeatureDefinition.GetXmlDefinition(SPHelper.CurrentCultureInfo);
                    XmlNodeList xmlList = xmlElement.SelectNodes(xpath, NamespaceManager);

                    foreach (XmlNode current in xmlList)
                    {
                        ListFeatureElementsFile elements;

                        string str2 = XmlHelper.GetAttribute(current, "Location");
                        if (!string.IsNullOrEmpty(str2))
                        {
                            string str3 = this.FeatureRelativeToAbsolutePath(str2);
                            string pathSubsumer = this.FeatureRelativeToAbsolutePath(null);
                            if (UrlPath.IsPathSubsumed(str3, pathSubsumer))
                            {
                                elements = new ListFeatureElementsFile(this, str2);
                                list.Add(elements);
                            }
                        }
                    }
                }
                this.elementFileList = list;
            }
        }
        #endregion // ElementFile List
 
        #region Properties
        public SPFeatureDefinition FeatureDefinition
        {
            get { return featureDefinition; }
            protected set { featureDefinition = value; }
        }

        public bool Exists
        {
            get { return FeatureDefinition != null; }
        }

        public string SettingsUrl
        {
            get
            {
                if (Exists)
                {
                    string url = GetPropertyValue("SettingsUrl");

                    if (!string.IsNullOrEmpty(url))
                        return url;
                }

                return "_layouts/ManageListFeatureSettings.aspx";
            }
        }

        public bool AllowMultipleInstances
        {
            get 
            { 
                return Helper.GetBooleanOrDefault(GetPropertyValue("AllowMultipleInstances"), true); 
            }
        }

        public string ManageUrl
        {
            get
            {
                return "_layouts/ManageListFeatures.aspx";
            }
        }

        #region TriggerEvent
        protected internal void TriggerEvent(ListFeatureEventReceiverType eventReceiverType, ListFeature feature)
        {
            TriggerEvent(eventReceiverType, new ListFeatureEventReceiverProperties(feature));
        }
        protected internal void TriggerEvent(ListFeatureEventReceiverType eventReceiverType, ListFeature feature, out ListFeatureEventReceiverProperties properties)
        {
            properties = new ListFeatureEventReceiverProperties(feature);
            TriggerEvent(eventReceiverType, properties);
        }
        protected internal void TriggerEvent(ListFeatureEventReceiverType eventReceiverType, ListFeatureDefinition definition, SPList list)
        {
            TriggerEvent(eventReceiverType, new ListFeatureEventReceiverProperties(definition, list));
        }
        protected internal void TriggerEvent(ListFeatureEventReceiverType eventReceiverType, ListFeatureDefinition definition, SPList list, out ListFeatureEventReceiverProperties properties)
        {
            properties = new ListFeatureEventReceiverProperties(definition, list);
            TriggerEvent(eventReceiverType, properties);
        }
        private void TriggerEvent(ListFeatureEventReceiverType eventReceiverType, ListFeatureEventReceiverProperties properties)
        {
            List<string> eventReceiverAdresses = GetPropertyValues("EventReceivers");

            foreach (string eventReceiverAdress in eventReceiverAdresses)
            {
                try
                {
                    ListFeatureEventReceiver eventReceiver
                     = WrapperObject.InstantiateObject<ListFeatureEventReceiver>(eventReceiverAdress);

                    switch (eventReceiverType)
                    {
                        case ListFeatureEventReceiverType.FeatureAdded:
                            eventReceiver.FeatureAdded(properties);
                            break;
                        case ListFeatureEventReceiverType.FeatureAdding:
                            eventReceiver.FeatureAdding(properties);
                            break;
                        case ListFeatureEventReceiverType.FeatureRemoving:
                            eventReceiver.FeatureRemoving(properties);
                            break;
                        case ListFeatureEventReceiverType.FeatureRemoved:
                            eventReceiver.FeatureRemoved(properties);
                            break;
                        case ListFeatureEventReceiverType.FeatureActivated:
                            eventReceiver.FeatureActivated(properties);
                            break;
                        case ListFeatureEventReceiverType.FeatureActivating:
                            eventReceiver.FeatureActivating(properties);
                            break;
                        case ListFeatureEventReceiverType.FeatureDeactivated:
                            eventReceiver.FeatureDeactivated(properties);
                            break;
                        case ListFeatureEventReceiverType.FeatureDeactivating:
                            eventReceiver.FeatureDeactivating(properties);
                            break;
                    }
                }
                catch { }
            }
            if (properties.Cancel && !string.IsNullOrEmpty(properties.CancelMessage))
            {
                throw new Exception(properties.CancelMessage);
            }
        }
        
        #endregion // TriggerEvent

        #region SPFeatureDefinition Properties
        public Guid Id
        {
            get { return featureDefinitionId; }
        }

        public string Version
        {
            get { if (Exists)return featureDefinition.Version.ToString(); else return string.Empty; }
        }

        public string GetTitle(CultureInfo ci)
        {
            if (Exists)
            {
                string title = featureDefinition.GetTitle(ci);
                if(!string.IsNullOrEmpty(title))
                    return title;
            }
            return SPResource.GetString("UntitledFeature", new object[0]);
        }

        public string GetDescription(CultureInfo ci)
        {
            if (Exists)
            {
                return featureDefinition.GetDescription(ci);
            }

            return string.Format(HttpContext.GetGlobalResourceObject("lf", "ListFeaturesErrorNotInstalled").ToString(), featureDefinitionId);
        }

        public string GetImageAltText(CultureInfo ci)
        {
            if (Exists)
            {
                return featureDefinition.GetImageAltText(ci);
            }
            return string.Empty;
        }

        public string GetImageUrl(CultureInfo ci)
        {
            if (Exists)
            {
                string url = featureDefinition.GetImageUrl(ci);

                if (!string.IsNullOrEmpty(url))
                    return url;
            }

            return "GenericFeature.gif";
        }
        #endregion // SPFeatureDefinition Properties

        #endregion // Properties

        #region Settings
        private ListFeatureSettings settings;
        public ListFeatureSettings Settings
        {
            get
            {

                if (settings == null)
                    settings = ListFeatureSettings.Create(this);
                return settings;
            }
        }
        #endregion // Settings

        #region Elements
        protected ListFeatureElements BuildElements()
        {
            ListFeatureElements definitions = new ListFeatureElements(this);

            foreach (ListFeatureElementsFile file in ElementFileList)
            {
                foreach (XmlNode node in file.GetElementNodes())
                {
                    if (node.NodeType == XmlNodeType.Element)
                        definitions.AddFromXml(node, file);
                }
            }
            return definitions;
        }
        private ListFeatureElements listFeatureElements;
        public ListFeatureElements Elements
        {
            get {

                if (listFeatureElements == null)
                    listFeatureElements = BuildElements();
                return listFeatureElements;
            }
        }

        public bool HasCustomizableElements
        {
            get 
            {
                bool hasCustomizableElements = false;

                List<ListFeatureElement> elements = Elements.Get();

                for (int i = 0; i < elements.Count && !hasCustomizableElements; i++)
                {
                    hasCustomizableElements |= elements[i].Customizable;
                }
                return hasCustomizableElements;
            }
        }
        #endregion // Elements



        #region ListFeature
        public ListFeature AddTo(SPList list)
        {
            ListFeature listFeature = new ListFeature();

            if (listFeature.Create(list, this, null))
                return listFeature;
            else
                return null;
        }

        public ListFeature AddTo(SPList list, InitListFeature initListFeature)
        {
            ListFeature listFeature = new ListFeature();

            if (listFeature.Create(list, this, initListFeature))
                return listFeature;
            else
                return null;
        }

        public bool CanBeAddedTo(SPList list)
        {
            return AllowMultipleInstances || ListFeature.GetListFeatures(list, this).Count == 0;
        }
        #endregion // ListFeature

        #region Static General Methods
        public static ListFeatureDefinition Current
        {
            get
            {
                return Instantiate(
                    SPContext.Current.Web,
                    new Guid(HttpContext.Current.Request["Feature"].ToString()));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="feature">Feature definition, cannot be null</param>
        /// <returns></returns>
        public static ListFeatureDefinition Instantiate(SPFeatureDefinition feature)
        {
            if (feature != null && IsListFeatureDefinition(feature))
            {
                return new ListFeatureDefinition(feature);
            }
            return null;
        }

        public static ListFeatureDefinition Instantiate(Guid guid)
        {
            return Instantiate(SPContext.Current.Web, guid);
        }

        public static ListFeatureDefinition Instantiate(SPWeb web, Guid guid)
        {
            List<SPFeatureDefinition> lstFeats = ListFeatureDefinition.GetFeatureDefinitions(web);

            foreach (SPFeatureDefinition featureDefinition in lstFeats)
            {
                if (featureDefinition.Id == guid && ListFeatureDefinition.IsListFeatureDefinition(featureDefinition))
                {
                    return new ListFeatureDefinition(featureDefinition);
                }
            }
            return new ListFeatureDefinition(guid);
        }

        public static List<ListFeatureDefinition> GetListFeaturesDefinitions(SPWeb web)
        {
            List<SPFeatureDefinition> lstFeats = ListFeatureDefinition.GetFeatureDefinitions(web);
            List<ListFeatureDefinition> newList = new List<ListFeatureDefinition>();

            foreach (SPFeatureDefinition featureDefinition in lstFeats)
            {
                ListFeatureDefinition newListFeatureDefinition =
                        ListFeatureDefinition.Instantiate(featureDefinition);

                if (newListFeatureDefinition != null)
                    newList.Add(newListFeatureDefinition);
            }
            return newList;
        }
        #endregion // Static General Methods

        #region Static SPFeatureDefinition Methods
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="web"></param>
        /// <returns>a list of FeatureDefinitions which are not null</returns>
        public static List<SPFeatureDefinition> GetFeatureDefinitions(SPWeb web)
        {
            List<SPFeatureDefinition> lstFeats = new List<SPFeatureDefinition>();


            foreach (SPFeature feature in web.Site.Features)
            {
                if(feature.Definition != null)
                    lstFeats.Add(feature.Definition);
            }

            foreach (SPFeature feature in web.Features)
            {
                if (feature.Definition != null)
                    lstFeats.Add(feature.Definition);
            }


            return lstFeats;
        }

        public static bool IsListFeatureDefinition(SPFeatureDefinition feature)
        {
            return feature.Properties[LFDKeyManager.KEY_FEATURE] != null;
        }
        #endregion // 

        #region Static Core Property Management
        public string GetPropertyValue(string key)
        {
            return GetPropertyValue(KeyManager, key);
        }

        protected internal string GetPropertyValue(KeyManager keyManager, string key)
        {
            return GetPropertyValue(keyManager.GenerateKey(key), FeatureDefinition);
        }

        protected internal static string GetPropertyValue(string key, SPFeatureDefinition featureDefinition)
        {
            SPFeatureProperty property = featureDefinition.Properties[key];

            if (property == null)
                return null;
            else
                return property.Value;
        }

        public List<string> GetPropertyValues(string startKey)
        {
            return GetPropertyValues(KeyManager, startKey);
        }

        protected internal List<string> GetPropertyValues(KeyManager keyManager, string startKey)
        {
            return GetPropertyValues(keyManager.GenerateKey(startKey), FeatureDefinition);
        }

        protected internal static List<string> GetPropertyValues(string startKey, SPFeatureDefinition featureDefinition)
        {
            List<string> values = new List<string>();

            if (featureDefinition != null)
            {
                for (int i = 0; i < featureDefinition.Properties.Count; i++)
                {
                    SPFeatureProperty property = featureDefinition.Properties[i] as SPFeatureProperty;
                    if (property.Name.StartsWith(startKey))
                        values.Add(property.Value);

                }
            }

            return values;
        }
        #endregion // Static Core Property Management


        #region Helpers
        public string FeatureRelativeToAbsolutePath(string pathRelative)
        {
            string rootDirectory = FeatureDefinition.RootDirectory;
            if (string.IsNullOrEmpty(pathRelative))
            {
                return rootDirectory;
            }
            return (rootDirectory + Path.DirectorySeparatorChar + pathRelative);
        }

        private XmlNamespaceManager m_nsmgr;
        protected XmlNamespaceManager NamespaceManager
        {
            get
            {
                if (this.m_nsmgr == null)
                {
                    XmlNameTable nameTable = new NameTable();
                    XmlNamespaceManager manager = new XmlNamespaceManager(nameTable);
                    manager.AddNamespace("wss", "http://schemas.microsoft.com/sharepoint/");
                    this.m_nsmgr = manager;
                }
                return this.m_nsmgr;
            }
        }

        #endregion // Helpers
    }
}
